package com.jdj.framework.dao.mybatis.utils;

import com.jdj.framework.core.base.utils.StringUtils;
import com.jdj.framework.core.spring.bean.BeanWrapper;
import com.jdj.framework.dao.mybatis.entity.BaseEntity;
import com.jdj.framework.dao.mybatis.entity.EntityPropertyDescriptor;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;
import org.springframework.beans.BeanWrapperImpl;

/**
 * @author caijinbang
 * @date 2019-04-14 12:10
 */
public class EntityUtil {

    /**
     * 获取 实体类 表名
     *
     * @param entityClass
     * @return
     */
    public static <Entity extends BaseEntity> String getTableName(Class<Entity> entityClass) {
        if (entityClass != null && entityClass.isAnnotationPresent(Table.class)) {
            return entityClass.getAnnotation(Table.class).name();
        } else {
            return null;
        }
    }

    /**
     * 获取 主键 字段名
     *
     * @param entityClass
     * @return
     */
    public static <Entity extends BaseEntity> String getPrimaryKeyFieldName(Class<Entity> entityClass) {
        //获取字段属性描述
        List<EntityPropertyDescriptor> entityPropertyDescriptors = getEntityPropertyDescriptor(entityClass);
        //
        String fieldName = null;
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.isPrimaryKey()) {
                fieldName = entityPropertyDescriptor.getFieldName();
                break;
            }
        }
        return fieldName;
    }

    /**
     * 获取 主键 字段列名
     * @param entityClass
     * @return
     */
    public static <Entity extends BaseEntity> String getPrimaryKeyColumnName(Class<Entity> entityClass) {
        //获取字段属性描述
        List<EntityPropertyDescriptor> entityPropertyDescriptors = getEntityPropertyDescriptor(entityClass);
        String columnName = null;
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.isPrimaryKey()) {
                columnName = entityPropertyDescriptor.getColumnName();
                break;
            }
        }
        return columnName;
    }

    /**
     * 获取 主键值
     *
     * @param entity
     * @return
     */
    public static <Entity extends BaseEntity> Serializable getPrimaryKeyValue(Entity entity) {
        return getFieldValueByFieldName(entity, getPrimaryKeyFieldName(entity.getClass()));
    }

    /**
     * 获取 版本 字段名
     * @param entityClass
     * @return
     */
    public static <Entity extends BaseEntity> String getVersionFileName(Class<Entity> entityClass) {
        //获取字段属性描述
        List<EntityPropertyDescriptor> entityPropertyDescriptors = getEntityPropertyDescriptor(entityClass);
        //
        String fileName = null;
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.isVersion()) {
                fileName = entityPropertyDescriptor.getFieldName();
                break;
            }
        }
        return fileName;
    }

    /**
     * 获取 版本 字段列名
     * @param entityClass
     * @return
     */
    public static <Entity extends BaseEntity> String getVersionColumnName(Class<Entity> entityClass) {
        //获取字段属性描述
        List<EntityPropertyDescriptor> entityPropertyDescriptors = getEntityPropertyDescriptor(entityClass);
        //
        String columnName = null;
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.isVersion()) {
                columnName = entityPropertyDescriptor.getColumnName();
                break;
            }
        }
        return columnName;
    }

    /**
     * 获取 版本值
     * @param entity
     * @return
     */
    public static <Entity extends BaseEntity> Serializable getVersionValue(Entity entity) {
        return getFieldValueByFieldName(entity, getVersionFileName(entity.getClass()));
    }

    /**
     * 根据 字段名 获取 字段get方法
     * @param entityClass
     * @param fieldName
     * @return
     */
    public static <Entity extends BaseEntity> Method getGetMethodByFieldName(Class<Entity> entityClass, String fieldName) {
        return EntityUtil.getPropertyDescriptorByFieldName(entityClass, fieldName).getGetMethod();
    }

    /**
     * 根据 字段名 获取 字段set方法
     *
     * @param entityClass
     * @param fieldName
     * @return
     */
    public static <Entity extends BaseEntity> Method getSetMethodByFieldName(Class<Entity> entityClass, String fieldName) {
        return EntityUtil.getPropertyDescriptorByFieldName(entityClass, fieldName).getSetMethod();
    }

    /**
     * 根据 字段名 获取 字段值
     *
     * @param entity
     * @param fieldName
     * @return
     */
    public static <Entity extends BaseEntity> Serializable getFieldValueByFieldName(Entity entity, String fieldName) {
        if (!StringUtils.isEmpty(fieldName)) {
            return (Serializable) new BeanWrapperImpl(entity).getPropertyValue(fieldName);
        } else {
            return null;
        }
    }

    /**
     * 根据 字段名 设置 字段值
     *
     * @param entity
     * @param fieldName
     * @return
     */
    public static <Entity extends BaseEntity> void setFieldValueByFieldName(Entity entity, String fieldName,
        Object value) {
        if (!StringUtils.isEmpty(fieldName)){
            new BeanWrapperImpl(entity).setPropertyValue(fieldName, value);
        }
    }

    /**
     * 获取 实体 所有 字段属性描述
     * @param clazz
     * @return
     */
    public static <Entity extends BaseEntity> List<EntityPropertyDescriptor> getEntityPropertyDescriptor(
        Class<Entity> clazz) {
        return EntityPropertyDescriptorManager.getEntityPropertyDescriptors(clazz);
    }

    /**
     * 根据 字段名 返回 字段属性描述
     * @param clazz
     * @param fieldName
     * @return
     */
    public static <Entity extends BaseEntity> EntityPropertyDescriptor getPropertyDescriptorByFieldName(
        Class<Entity> clazz, String fieldName) {
        return EntityPropertyDescriptorManager.getPropertyDescriptorByFieldName(clazz, fieldName);
    }

    /**
     * 根据 字段列名 返回 字段属性描述
     * @param clazz
     * @param columnName
     * @return
     */
    public static <Entity extends BaseEntity> EntityPropertyDescriptor getPropertyDescriptorByColumnName(
        Class<Entity> clazz, String columnName) {
        return EntityPropertyDescriptorManager.getPropertyDescriptorByColumnName(clazz, columnName);
    }

    /**
     * 根据 字段名 返回 字段列名
     * @param clazz
     * @param fieldName
     * @return
     */
    public static <Entity extends BaseEntity> String getColumnNameByFieldName(Class<Entity> clazz, String fieldName) {
        EntityPropertyDescriptor entityPropertyDescriptor = EntityPropertyDescriptorManager
            .getPropertyDescriptorByFieldName(clazz, fieldName);
        return entityPropertyDescriptor.getColumnName();
    }

    /**
     * 根据 字段列名 返回 字段名
     * @param clazz
     * @param columnName
     * @return
     */
    public static <Entity extends BaseEntity> String getFieldNameByColumnName(Class<Entity> clazz, String columnName) {
        EntityPropertyDescriptor entityPropertyDescriptor = EntityPropertyDescriptorManager
            .getPropertyDescriptorByColumnName(clazz, columnName);
        return entityPropertyDescriptor.getFieldName();
    }

    /**
     * 根据 方法名 返回 方法
     *
     * @param clazz
     * @param methodName
     * @return
     */
    public static <Entity extends BaseEntity> Method getMethodByMethodName(Class<Entity> clazz, String methodName) {
        return EntityPropertyDescriptorManager.getMethodByMethodName(clazz, methodName);
    }

    /**
     * 批量指定 设值字段 数组
     * 	已经标识设置过值的字段将被清空
     * 	设置传人的字段名数组
     *
     * @param entities
     * @param fieldNameArr
     */
    public static <Entity extends BaseEntity> void setSettedFieldArr(List<Entity> entities, String[] fieldNameArr) {
        for (Entity entity : entities) {
            entity.setupSettedFieldArr(fieldNameArr);
        }
    }

    /**
     * 根据给定的类，获取所有字段名
     * @param clazz
     * @return
     */
    public static <Entity extends BaseEntity> List<String> getAllFieldName(Class<Entity> clazz) {
        List<EntityPropertyDescriptor> entityPropertyDescriptors = EntityPropertyDescriptorManager
            .getEntityPropertyDescriptors(clazz);
        List<String> fieldNameList = new ArrayList<String>();
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.getFieldName() != null) {
                fieldNameList.add(entityPropertyDescriptor.getFieldName());
            }
        }
        return fieldNameList;
    }

    /**
     * 根据给定的类，获取所有字段列名
     * @param clazz
     * @return
     */
    public static <Entity extends BaseEntity> List<String> getAllColumnName(Class<Entity> clazz) {
        List<EntityPropertyDescriptor> entityPropertyDescriptors = EntityPropertyDescriptorManager
            .getEntityPropertyDescriptors(clazz);
        List<String> columnNameList = new ArrayList<String>();
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.getColumnName() != null) {
                columnNameList.add(entityPropertyDescriptor.getColumnName());
            }
        }
        return columnNameList;
    }

    /**
     * 根据给定的类，获取key为数据库字段名，value为类的字段名
     * @param clazz
     * @return
     */
    public static <Entity extends BaseEntity> Map<String, String> getColumnFieldMap(Class<Entity> clazz) {
        List<EntityPropertyDescriptor> entityPropertyDescriptors = EntityPropertyDescriptorManager
            .getEntityPropertyDescriptors(clazz);
        Map<String, String> columnFieldMap = new HashMap<String, String>();
        for (EntityPropertyDescriptor entityPropertyDescriptor : entityPropertyDescriptors) {
            if (entityPropertyDescriptor.getColumnName() != null) {
                columnFieldMap.put(entityPropertyDescriptor.getColumnName(), entityPropertyDescriptor.getFieldName());
            }
        }
        return columnFieldMap;
    }

    /**
     * Entity属性管理类
     * @author xiezm
     *
     */
    static class EntityPropertyDescriptorManager {

        /**
         * 实体所有属性描述（字段、set方法、get方法、其它方法）
         */
        private transient static Map<Class<? extends BaseEntity>, List<EntityPropertyDescriptor>> propertyDescriptorMap = new HashMap<Class<? extends BaseEntity>, List<EntityPropertyDescriptor>>();

        /**
         * 获取实体所有属性描述
         * @param clazz
         * @return
         */
        public static synchronized <Entity extends BaseEntity> List<EntityPropertyDescriptor> getEntityPropertyDescriptors(
            Class<Entity> clazz) {
            if (propertyDescriptorMap.containsKey(clazz)){
                return propertyDescriptorMap.get(clazz);
            }else {
                List<EntityPropertyDescriptor> propertyDescriptors = new ArrayList<EntityPropertyDescriptor>();
                BeanWrapper beanWrapper = new BeanWrapper(clazz);
                //处理实体（有set，get方法）的字段属性
                for (PropertyDescriptor propertyDescriptor : beanWrapper.getPropertyDescriptorsInDeclaringOrder()) {
                    //判断是否为可读属性
                    if (beanWrapper.isReadableProperty(propertyDescriptor.getName())) {
                        //字段属性描述
                        EntityPropertyDescriptor entityPropertyDescriptor = new EntityPropertyDescriptor(
                            propertyDescriptor.getName(), propertyDescriptor.getWriteMethod(),
                            propertyDescriptor.getReadMethod());
                        //判断是否有@Column注解的set方法
                        if (propertyDescriptor.getReadMethod().isAnnotationPresent(Column.class)) {
                            Column column = propertyDescriptor.getReadMethod().getAnnotation(Column.class);
                            //持久化字段
                            //字段列名
                            entityPropertyDescriptor.setColumnName(column.name());
                            //是否为主键字段
                            if (propertyDescriptor.getReadMethod().isAnnotationPresent(Id.class)) {
                                entityPropertyDescriptor.setPrimaryKey(true);
                            }
                            //是否为版本字段
                            if (propertyDescriptor.getReadMethod().isAnnotationPresent(Version.class)) {
                                entityPropertyDescriptor.setVersion(true);
                            }
                        } else {
                            //非持久化字段
                            entityPropertyDescriptor.setTransient(true);
                        }
                        propertyDescriptors.add(entityPropertyDescriptor);
                    }
                }
                //处理没有对应字段属性（set、get方法），的其它方法
                for (Method metohd : beanWrapper.getPropertyDescriptorsNotInDeclaringOrder()) {
                    EntityPropertyDescriptor entityPropertyDescriptor = new EntityPropertyDescriptor();
                    //实体其它方法
                    entityPropertyDescriptor.setOtherMethod(metohd);
                    propertyDescriptors.add(entityPropertyDescriptor);
                }
                //装入静态map
                propertyDescriptorMap.put(clazz, propertyDescriptors);
                //返回
                return propertyDescriptorMap.get(clazz);
            }
        }

        /**
         * 根据 字段名 返回 实体字段属性描述
         * @param clazz
         * @param fieldName
         * @return
         */
        public static synchronized <Entity extends BaseEntity> EntityPropertyDescriptor getPropertyDescriptorByFieldName(
            Class<Entity> clazz, String fieldName) {
            for (EntityPropertyDescriptor propertyDescriptor : getEntityPropertyDescriptors(clazz)) {
                if (propertyDescriptor.getFieldName() != null && propertyDescriptor.getFieldName().equals(fieldName)){
                    return propertyDescriptor;
                }
            }
            return null;
        }

        /**
         * 根据 字段列名 返回 实体字段属性描述
         * @param clazz
         * @param columnName
         * @return
         */
        public static synchronized <Entity extends BaseEntity> EntityPropertyDescriptor getPropertyDescriptorByColumnName(
            Class<Entity> clazz, String columnName) {
            for (EntityPropertyDescriptor propertyDescriptor : getEntityPropertyDescriptors(clazz)) {
                if (propertyDescriptor.getColumnName() != null && propertyDescriptor.getColumnName().equals(columnName)){
                    return propertyDescriptor;
                }
            }
            return null;
        }

        /**
         * 根据 字段列名 返回 实体字段属性描述
         * @param clazz
         * @param methodName
         * @return
         */
        public static synchronized <Entity extends BaseEntity> Method getMethodByMethodName(Class<Entity> clazz,
            String methodName) {
            Method method = null;
            for (EntityPropertyDescriptor propertyDescriptor : getEntityPropertyDescriptors(clazz)) {
                if (propertyDescriptor.getGetMethod() != null
                    && propertyDescriptor.getGetMethod().getName().equals(methodName)) {
                    method = propertyDescriptor.getGetMethod();
                }
                if (propertyDescriptor.getSetMethod() != null
                    && propertyDescriptor.getSetMethod().getName().equals(methodName)) {
                    method = propertyDescriptor.getSetMethod();
                }
                if (propertyDescriptor.getOtherMethod() != null
                    && propertyDescriptor.getOtherMethod().getName().equals(methodName)) {
                    method = propertyDescriptor.getOtherMethod();
                }
            }
            return method;
        }
    }

}
